"""
硬件接口模块，模拟与道闸设备的通信
"""

import os
import logging
import time
import random
import threading
import socket
import json
from datetime import datetime
import queue
from database import execute_non_query, get_connection

# 配置日志
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger('hardware_interface')

# 道闸命令常量
CMD_OPEN_GATE = "OPEN"
CMD_CLOSE_GATE = "CLOSE"
CMD_CHECK_STATUS = "STATUS"
CMD_RESET = "RESET"
CMD_TAKE_PHOTO = "PHOTO"

# 道闸状态常量
STATUS_OPEN = "OPEN"
STATUS_CLOSED = "CLOSED"
STATUS_ERROR = "ERROR"
STATUS_RESETTING = "RESETTING"

class GateDevice:
    """模拟道闸设备"""
    
    def __init__(self, gate_id, ip_address="127.0.0.1", port=10000 + random.randint(1, 1000)):
        self.gate_id = gate_id
        self.ip_address = ip_address
        self.port = port
        self.status = STATUS_CLOSED
        self.last_command = None
        self.last_command_time = None
        self.error_rate = 0.05  # 5%概率出错
        self.is_running = False
        self.command_queue = queue.Queue()
        self.server_socket = None
        
        # 获取道闸信息
        self._load_gate_info()
    
    def _load_gate_info(self):
        """从数据库加载道闸信息"""
        try:
            conn = get_connection()
            cursor = conn.cursor()
            
            query = """
            SELECT GD.ParkingLotID, PL.ParkingLotName, GD.GateLocation, GD.GateType
            FROM GateDevice GD
            JOIN ParkingLot PL ON GD.ParkingLotID = PL.ParkingLotID
            WHERE GD.GateID = ?
            """
            
            cursor.execute(query, (self.gate_id,))
            row = cursor.fetchone()
            
            if row:
                self.parking_lot_id = row[0]
                self.parking_lot_name = row[1]
                self.gate_location = row[2]
                self.gate_type = row[3]
                logger.info(f"已加载道闸信息: ID={self.gate_id}, 位置={self.gate_location}, 类型={self.gate_type}")
            else:
                logger.error(f"未找到ID为{self.gate_id}的道闸信息")
                raise ValueError(f"道闸ID {self.gate_id} 不存在")
            
            cursor.close()
            conn.close()
        except Exception as e:
            logger.error(f"加载道闸信息失败: {str(e)}")
            raise
    
    def start(self):
        """启动模拟设备服务器"""
        if self.is_running:
            return
        
        self.is_running = True
        self.server_thread = threading.Thread(target=self._run_server)
        self.server_thread.daemon = True
        self.server_thread.start()
        
        self.processor_thread = threading.Thread(target=self._process_commands)
        self.processor_thread.daemon = True
        self.processor_thread.start()
        
        logger.info(f"道闸设备 {self.gate_id} ({self.gate_location}) 已启动，监听 {self.ip_address}:{self.port}")
    
    def stop(self):
        """停止模拟设备服务器"""
        self.is_running = False
        if self.server_socket:
            self.server_socket.close()
        logger.info(f"道闸设备 {self.gate_id} ({self.gate_location}) 已停止")
    
    def _run_server(self):
        """运行TCP服务器监听命令"""
        try:
            self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            self.server_socket.bind((self.ip_address, self.port))
            self.server_socket.settimeout(1.0)  # 1秒超时，允许优雅退出
            self.server_socket.listen(5)
            
            while self.is_running:
                try:
                    client_socket, addr = self.server_socket.accept()
                    client_thread = threading.Thread(
                        target=self._handle_client,
                        args=(client_socket, addr)
                    )
                    client_thread.daemon = True
                    client_thread.start()
                except socket.timeout:
                    continue
                except Exception as e:
                    if self.is_running:  # 只在正常运行时报告错误
                        logger.error(f"接受连接时出错: {str(e)}")
        except Exception as e:
            logger.error(f"启动服务器时出错: {str(e)}")
        finally:
            if self.server_socket:
                self.server_socket.close()
    
    def _handle_client(self, client_socket, address):
        """处理客户端连接"""
        try:
            logger.info(f"接收来自 {address} 的连接")
            
            data = client_socket.recv(1024).decode('utf-8').strip()
            if not data:
                return
            
            logger.info(f"收到命令: {data}")
            
            try:
                command_data = json.loads(data)
                command = command_data.get('command')
                params = command_data.get('params', {})
                
                # 将命令加入队列处理
                self.command_queue.put((command, params))
                
                # 立即发送确认接收
                response = {
                    'status': 'received',
                    'message': f'Command {command} queued for processing'
                }
                client_socket.send(json.dumps(response).encode('utf-8'))
                
            except json.JSONDecodeError:
                logger.error(f"无效的JSON数据: {data}")
                response = {
                    'status': 'error',
                    'message': 'Invalid JSON data'
                }
                client_socket.send(json.dumps(response).encode('utf-8'))
                
        except Exception as e:
            logger.error(f"处理客户端连接时出错: {str(e)}")
        finally:
            client_socket.close()
    
    def _process_commands(self):
        """处理命令队列"""
        while self.is_running:
            try:
                command, params = self.command_queue.get(timeout=1.0)
                self._execute_command(command, params)
                self.command_queue.task_done()
            except queue.Empty:
                continue
            except Exception as e:
                logger.error(f"处理命令时出错: {str(e)}")
    
    def _execute_command(self, command, params):
        """执行命令"""
        self.last_command = command
        self.last_command_time = datetime.now()
        
        # 模拟随机故障
        if random.random() < self.error_rate and command != CMD_RESET:
            logger.warning(f"道闸 {self.gate_id} 执行 {command} 命令时发生故障")
            self.status = STATUS_ERROR
            self._record_error(f"执行{command}命令失败")
            return
        
        if command == CMD_OPEN_GATE:
            logger.info(f"道闸 {self.gate_id} 正在打开")
            time.sleep(1.5)  # 模拟打开时间
            self.status = STATUS_OPEN
            
            # 如果是入口道闸，记录车辆进入
            if self.gate_type in ('Entry', 'Both') and 'vehicle_number' in params:
                self._record_vehicle_entry(params.get('vehicle_number'), params.get('vehicle_type', '小型车'))
            
            # 如果是出口道闸，记录车辆离开
            if self.gate_type in ('Exit', 'Both') and 'vehicle_number' in params:
                self._record_vehicle_exit(params.get('vehicle_number'))
            
            logger.info(f"道闸 {self.gate_id} 已打开")
            
            # 自动关闭
            timer = threading.Timer(5.0, self._auto_close)
            timer.daemon = True
            timer.start()
            
        elif command == CMD_CLOSE_GATE:
            logger.info(f"道闸 {self.gate_id} 正在关闭")
            time.sleep(1.0)  # 模拟关闭时间
            self.status = STATUS_CLOSED
            logger.info(f"道闸 {self.gate_id} 已关闭")
            
        elif command == CMD_CHECK_STATUS:
            logger.info(f"道闸 {self.gate_id} 状态: {self.status}")
            
        elif command == CMD_RESET:
            logger.info(f"道闸 {self.gate_id} 正在重置")
            self.status = STATUS_RESETTING
            time.sleep(3.0)  # 模拟重置时间
            self.status = STATUS_CLOSED
            logger.info(f"道闸 {self.gate_id} 已重置")
            
        elif command == CMD_TAKE_PHOTO:
            logger.info(f"道闸 {self.gate_id} 正在拍照")
            # 模拟拍照识别
            if 'simulate_recognition' in params and params['simulate_recognition']:
                vehicle_number = f"模拟车牌{random.randint(10000, 99999)}"
                logger.info(f"道闸 {self.gate_id} 识别到车牌: {vehicle_number}")
                return vehicle_number
            logger.info(f"道闸 {self.gate_id} 拍照完成")
    
    def _auto_close(self):
        """自动关闭道闸"""
        if self.status == STATUS_OPEN:
            logger.info(f"道闸 {self.gate_id} 自动关闭")
            self.status = STATUS_CLOSED
    
    def _record_vehicle_entry(self, vehicle_number, vehicle_type):
        """记录车辆进入"""
        try:
            sql = """
            INSERT INTO VehicleEntryExit 
            (ParkingLotID, VehicleNumber, EntryTime, GateEntryID, VehicleType)
            VALUES (?, ?, GETDATE(), ?, ?)
            """
            
            execute_non_query(sql, (self.parking_lot_id, vehicle_number, self.gate_id, vehicle_type))
            logger.info(f"已记录车辆 {vehicle_number} 进入停车场 {self.parking_lot_name}")
        except Exception as e:
            logger.error(f"记录车辆进入失败: {str(e)}")
    
    def _record_vehicle_exit(self, vehicle_number):
        """记录车辆离开"""
        try:
            # 查找未结束的记录
            conn = get_connection()
            cursor = conn.cursor()
            
            query = """
            SELECT TOP 1 RecordID 
            FROM VehicleEntryExit 
            WHERE VehicleNumber = ? AND ExitTime IS NULL 
            ORDER BY EntryTime DESC
            """
            
            cursor.execute(query, (vehicle_number,))
            row = cursor.fetchone()
            
            if row:
                record_id = row[0]
                
                # 更新离场时间和出口道闸ID
                update_sql = """
                UPDATE VehicleEntryExit
                SET ExitTime = GETDATE(),
                    GateExitID = ?,
                    PaymentAmount = CASE 
                        WHEN PricingRules.BasePricePerHour IS NULL THEN NULL
                        ELSE 
                            CASE 
                                WHEN CEILING(DATEDIFF(MINUTE, EntryTime, GETDATE()) / 60.0) * PricingRules.BasePricePerHour > PricingRules.MaximumDailyFee 
                                THEN PricingRules.MaximumDailyFee 
                                ELSE CEILING(DATEDIFF(MINUTE, EntryTime, GETDATE()) / 60.0) * PricingRules.BasePricePerHour 
                            END
                    END,
                    PaymentStatus = '已支付'
                FROM VehicleEntryExit
                LEFT JOIN PricingRules ON VehicleEntryExit.ParkingLotID = PricingRules.ParkingLotID 
                    AND VehicleEntryExit.VehicleType = PricingRules.VehicleType
                    AND GETDATE() BETWEEN PricingRules.EffectiveFrom AND ISNULL(PricingRules.EffectiveTo, '9999-12-31')
                WHERE RecordID = ?
                """
                
                cursor.execute(update_sql, (self.gate_id, record_id))
                conn.commit()
                logger.info(f"已记录车辆 {vehicle_number} 离开停车场 {self.parking_lot_name}")
            else:
                logger.warning(f"未找到车辆 {vehicle_number} 的进场记录")
            
            cursor.close()
            conn.close()
            
        except Exception as e:
            logger.error(f"记录车辆离开失败: {str(e)}")
    
    def _record_error(self, error_description):
        """记录设备故障"""
        try:
            sql = """
            INSERT INTO DeviceErrors 
            (GateID, ErrorTime, ErrorDescription)
            VALUES (?, GETDATE(), ?)
            """
            
            execute_non_query(sql, (self.gate_id, error_description))
            logger.info(f"已记录道闸 {self.gate_id} 故障: {error_description}")
        except Exception as e:
            logger.error(f"记录设备故障失败: {str(e)}")

class GateController:
    """道闸控制器，用于与模拟的道闸设备通信"""
    
    def __init__(self):
        self.devices = {}  # 存储已连接的设备
    
    def connect_device(self, gate_id, ip_address="127.0.0.1", port=None):
        """连接到道闸设备"""
        if not port:
            # 如果未指定端口，使用门ID加10000作为默认端口
            port = 10000 + gate_id
        
        device_key = f"{ip_address}:{port}"
        
        if device_key in self.devices:
            logger.info(f"已经连接到道闸设备 {gate_id}")
            return self.devices[device_key]
        
        try:
            # 创建模拟设备并启动
            device = GateDevice(gate_id, ip_address, port)
            device.start()
            
            self.devices[device_key] = device
            logger.info(f"已连接到道闸设备 {gate_id}, 位置: {device.gate_location}")
            return device
        except Exception as e:
            logger.error(f"连接道闸设备 {gate_id} 失败: {str(e)}")
            raise
    
    def send_command(self, device, command, params=None):
        """发送命令到道闸设备"""
        if params is None:
            params = {}
        
        if isinstance(device, GateDevice):
            gate_id = device.gate_id
            ip_address = device.ip_address
            port = device.port
        else:
            gate_id = device
            device_info = self._get_device_info(gate_id)
            if not device_info:
                raise ValueError(f"未找到ID为{gate_id}的道闸设备")
            
            ip_address = device_info['ip_address']
            port = device_info['port']
        
        try:
            # 创建到设备的TCP连接
            client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            client_socket.settimeout(5.0)
            client_socket.connect((ip_address, port))
            
            # 准备命令
            command_data = {
                'command': command,
                'params': params
            }
            
            # 发送命令
            client_socket.send(json.dumps(command_data).encode('utf-8'))
            
            # 接收响应
            response = client_socket.recv(1024).decode('utf-8')
            
            logger.info(f"向道闸 {gate_id} 发送命令 {command}, 响应: {response}")
            
            try:
                return json.loads(response)
            except json.JSONDecodeError:
                return {'status': 'error', 'message': '无效的响应格式'}
            
        except Exception as e:
            logger.error(f"发送命令到道闸 {gate_id} 失败: {str(e)}")
            return {'status': 'error', 'message': str(e)}
        finally:
            client_socket.close()
    
    def open_gate(self, gate_id, vehicle_number=None, vehicle_type='小型车'):
        """打开道闸"""
        params = {}
        if vehicle_number:
            params['vehicle_number'] = vehicle_number
            params['vehicle_type'] = vehicle_type
        
        return self.send_command(gate_id, CMD_OPEN_GATE, params)
    
    def close_gate(self, gate_id):
        """关闭道闸"""
        return self.send_command(gate_id, CMD_CLOSE_GATE)
    
    def check_status(self, gate_id):
        """检查道闸状态"""
        return self.send_command(gate_id, CMD_CHECK_STATUS)
    
    def reset_gate(self, gate_id):
        """重置道闸"""
        return self.send_command(gate_id, CMD_RESET)
    
    def take_photo(self, gate_id, simulate_recognition=False):
        """触发拍照"""
        params = {'simulate_recognition': simulate_recognition}
        return self.send_command(gate_id, CMD_TAKE_PHOTO, params)
    
    def disconnect_all(self):
        """断开所有连接的设备"""
        for device_key, device in list(self.devices.items()):
            try:
                device.stop()
                logger.info(f"已断开与道闸设备 {device.gate_id} 的连接")
            except Exception as e:
                logger.error(f"断开道闸设备 {device.gate_id} 连接失败: {str(e)}")
        
        self.devices.clear()
    
    def _get_device_info(self, gate_id):
        """获取设备连接信息"""
        for device_key, device in self.devices.items():
            if device.gate_id == gate_id:
                return {
                    'ip_address': device.ip_address,
                    'port': device.port
                }
        return None

def demo_device_communication():
    """演示与道闸设备的通信"""
    try:
        # 创建控制器
        controller = GateController()
        
        # 连接设备 (ID为1的道闸)
        device = controller.connect_device(1)
        print(f"已连接到道闸: {device.gate_location} (类型: {device.gate_type})")
        
        # 检查状态
        controller.check_status(1)
        
        # 模拟车辆进入
        time.sleep(1)
        print("\n模拟车辆进入...")
        controller.open_gate(1, "京A12345", "小型车")
        
        # 等待一会，展示自动关闭
        time.sleep(6)
        
        # 模拟车辆离开
        time.sleep(1)
        print("\n模拟车辆离开...")
        controller.open_gate(1, "京A12345")
        
        # 等待一会，展示自动关闭
        time.sleep(6)
        
        # 测试重置
        print("\n重置道闸...")
        controller.reset_gate(1)
        time.sleep(3)
        
        # 断开连接
        controller.disconnect_all()
        print("演示完成")
        
    except Exception as e:
        print(f"演示过程中出错: {str(e)}")

if __name__ == "__main__":
    # 测试硬件接口模拟
    demo_device_communication() 